package com.ease.util {

   import flash.events.TimerEvent;
   import flash.utils.Timer;

   import mx.managers.LayoutManager;

   /**
    * Tracks pending rendering operations.
    *
    * Typically, the <code>track</code> method is called after an operation known to
    * initiate rendering operations, e.g. adding a component to the display, when the
    * pending rendering operations are completed, the caller is notified.
    */

   public class RenderingTracker {
      private static var _instance:RenderingTracker;

      private var _timer:Timer;
      private var _displayStable:Boolean;
      private var _callbacksInvoker:QueuingFunctionCaller;

      /**
       * The timer delay in milliseconds.
       * invalid LayoutManager state is checked at this time interval.
       */
      private static const STABLE_DISPLAY_DELAY:int = 30;

      /**
       * @private Constructor. As a singleton, this class constructor prevents
       * instantiation from other classes. Use RenderingTracker.instance()
       * to work with the singleton from other classes.
       *
       * @throws Error if the constructor is called by an external class or if
       *    the singelton has already been instantiated.
       */
      public function RenderingTracker(token:Class) {
         if (token != PrivateConstructorToken) {
            throw new Error("RenderingTracker cannot be instantiated directly.");
         }
      }

      /**
       * Retrieves the singleton instance of RenderingTracker.
       */
      public static function get instance():RenderingTracker {
         if (_instance == null) {
            _instance = new RenderingTracker(PrivateConstructorToken);
         }
         return _instance;
      }

      /**
       * Tests whether there are rendering pending operations.
       */
      public function get renderingPending():Boolean {
         return LayoutManager.getInstance().isInvalid();
      }

      /**
       * Tracks the rendering state.
       * When no rendering operation is pending, the specified callback function is invoked.
       *
       * @param callback
       *    <code>function(callContext:Object):void</code>, called when no rendering
       *    operation is pending.
       *
       * @param callContext
       *    The callback context passed to the callback function.
       */
      public function track(callback:Function, callContext:Object = null):void {
         if (_timer == null) {
            _timer = new Timer(STABLE_DISPLAY_DELAY);
            _timer.addEventListener(TimerEvent.TIMER, onTimer);
            _timer.start();
            _callbacksInvoker = new QueuingFunctionCaller();
            _displayStable = false
         }
         _callbacksInvoker.enqueue(callback, [callContext]);
      }

      private function onTimer(event:TimerEvent):void {
         if (this.renderingPending) {
            _displayStable = false;
            return;
         }
         // The display is considered stable if no rendering operations are initiated
         // during the delay interval.
         if (!_displayStable) {
            _displayStable = true;
            return;
         }
         _timer.removeEventListener(TimerEvent.TIMER, onTimer);
         _timer.stop();
         _timer = null;

         var invoker:QueuingFunctionCaller = _callbacksInvoker;
         _callbacksInvoker = null;
         invoker.callPendingFunctions();
      }
   }
}

class PrivateConstructorToken {}